package com.wuyan.web.form.api;

import com.alibaba.excel.EasyExcel;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wuyan.web.base.aop.ApiLogAnnotation;
import com.wuyan.web.base.helper.BaseApi;
import com.wuyan.web.base.helper.auth.LoginInfo;
import com.wuyan.web.base.helper.rep.RepBody;
import com.wuyan.web.base.helper.rep.RepCodeEnum;
import com.wuyan.web.base.helper.rep.RepHelper;
import com.wuyan.web.form.entity.PubForm;
import com.wuyan.web.form.helper.easyexcel.NoModelDataListener;
import com.wuyan.web.form.repo.PubFormRepoExtend;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;

import static com.wuyan.web.form.service.FormService.*;

/**
 * 导入数据
 */

@Slf4j
@RestController
@RequestMapping("/api/import")
public class ImportApi extends BaseApi implements RepHelper {

    private static final Set<String> KEY_ID_NAME_GROUP = new HashSet<>();

    static {
        KEY_ID_NAME_GROUP.add("ID");
        KEY_ID_NAME_GROUP.add("主键");
        KEY_ID_NAME_GROUP.add("id");
    }


    @Autowired
    private FormApi formApi;

    @Autowired
    private PubFormRepoExtend pubFormRepoExtend;

    @Autowired
    private ObjectMapper mapper;

    /**
     * 文件上传：同时支持多文件
     *
     * @return RepBody<PubFile>
     */
    @PostMapping(value = "/{table}")
    @ApiLogAnnotation(name = "FormData:import")
    public RepBody<Object> importTable(HttpServletRequest request,
                                       @PathVariable(value = "table") String table)
            throws JsonProcessingException {
        MultipartHttpServletRequest multipartRequest;
        List<MultipartFile> files = null;

        if (request instanceof MultipartHttpServletRequest) {
            multipartRequest = (MultipartHttpServletRequest) (request);
            files = multipartRequest.getFiles("file");
        }

        if (files != null && !files.isEmpty()) {
            LoginInfo loginInfo = getLoginInfo(request);

            // 先获取表单信息
            PubForm pubForm = pubFormRepoExtend.findFirstByFormRefAndStatusAndDisabled(table, 1, 0);
            if (null == pubForm) {
                return error(RepCodeEnum.ERR_NO_FORM);
            }

            // 解析字段信息
            String fields = pubForm.getFields();
            List<Map> listFields = mapper.readValue(fields, new TypeReference<List<Map>>() {
            });

            /*
             * 遍历所有文件，依次解析并保存到数据库
             */
            files.forEach(file -> {
                try {
                    EasyExcel.read(file.getInputStream(),
                            new NoModelDataListener((sheetName, data, firstData, row) -> {
                                // 先判断是否配置了强匹配工作表
                                if (pubForm.getExcelSheetStrictMatch() == 1 && !sheetName.equals(pubForm.getName())) {
                                    return false;
                                }

                                // 格式化数据
                                List<Map<String, Object>> list =
                                        convertData(data, firstData, row, pubForm, listFields, loginInfo);

                                // 保存数据
                                RepBody<Integer> res = formApi.plist(request, list, pubForm.getFormRef());
                                return res.getCode() == RepCodeEnum.OK.getCode();
                            })).doReadAll();
                } catch (IOException e) {
                    log.error(e.getMessage(), e);
                }
            });
        }

        return ok(RepCodeEnum.OK);
    }

    /**
     * 转换数据
     *
     * @param data       数据
     * @param firstData  首次提交的数据
     * @param rowNum     当前行
     * @param pubForm    表单配置
     * @param listFields 字段配置
     * @param loginInfo  用户信息
     * @return List<Map < String, Object>>
     */
    private List<Map<String, Object>> convertData(List<Map<Integer, String>> data,
                                                  List<Map<Integer, String>> firstData,
                                                  AtomicInteger rowNum,
                                                  PubForm pubForm,
                                                  List<Map> listFields,
                                                  LoginInfo loginInfo) {
        // 结果
        List<Map<String, Object>> res = new ArrayList<>();
        // 首条数据所在的行位置
        int dataRowStart = pubForm.getExcelDataRowStart();
        if (rowNum.get() < dataRowStart) {
            return res;
        }

        int batchCount = NoModelDataListener.BATCH_COUNT;
        boolean isFirst = rowNum.get() <= batchCount;

        // 标签对应的位置
        Map<String, String[]> fieldPosition = new HashMap<>();
        // 每一列对应的字段名
        Map<Integer, String> colFields = new HashMap<>();
        // 通用数据
        Map<String, Object> absRes = new HashMap<>();
        absRes.put(DEF_CFG_SOURCE_NAME, "import");
        absRes.put(DEF_CREATOR_ID_NAME, null == loginInfo ? 0 : loginInfo.getAccount().getId());
        absRes.put(DEF_CREATOR_NAME_NAME, null == loginInfo ? 0 : loginInfo.getUser().getNickname());

        /*
         * 先计算数据标签位置
         */
        listFields.forEach(field -> {
            Map config = (Map) field.get("__config__");
            String position = config.containsKey("excelPosition") ? config.get("excelPosition").toString() : "";
            String modelName = field.get("__vModel__").toString();

            if (StringUtils.isBlank(position)) {
                fieldPosition.put(modelName, new String[0]);
            } else {
                String[] point = position.split(",");
                if (point.length < 2) {
                    return;
                }

                // 取出对应的行列,暂时存储
                int row = Integer.parseInt(point[1]);
                int col = Integer.parseInt(point[0]);
                fieldPosition.put(modelName, point);
                colFields.put(col, modelName);

                // 绝对位置,设置通用数据
                if (point.length > 2 && "true".equals(point[2])) {
                    /*
                        行不在范围内,则忽略
                     */
                    int rowBatchCount = row / batchCount;
                    int rowNumBatchCount = rowNum.get() / batchCount;
                    int rowNumMod = rowNum.get() % batchCount;

                    if (row > rowNum.get() // 小了
                            || (rowBatchCount != rowNumBatchCount && rowNumMod != 0) // 不能整除
                            || ((rowBatchCount != rowNumBatchCount - 1) && rowNumMod == 0)) // 刚好整除
                    {
                        return;
                    }

                    // 计算在当前data中的位置, 因为单元格填写从1开始,因此需要减1
                    int index = row % batchCount - 1;
                    Map<Integer, String> absData = data.get(index);
                    absRes.put(modelName, absData.get(col));
                }
            }
        });

        // 特殊的值标签位置计算
        AtomicInteger findHeadNum = new AtomicInteger(1);
        // 因为单元格填写从1开始,因此需要减1
        int dataRowStartIndex = dataRowStart - 1;
        for (int i = 0; i < dataRowStartIndex && findHeadNum.get() > 0; i++) {
            Map<Integer, String> item = isFirst ? data.get(i) : firstData.get(i);
            int finalI = i;
            item.forEach((key, value) -> {
                // ID列存在
                if (KEY_ID_NAME_GROUP.contains(value)) {
                    colFields.put(key, "id");
                    fieldPosition.put("id", new String[]{String.valueOf(key), (finalI + 1) + ""});
                    findHeadNum.getAndDecrement();
                }
            });
        }

        // 当前所在的数据行
        AtomicInteger dataRow = new AtomicInteger(isFirst ? 0 : rowNum.get() - batchCount + 1);
        /*
         * 遍历数据行,挨个处理
         */
        data.forEach(item -> {
            // 如果当前行小于首个数据行,则不执行下面操作
            if (dataRow.get() < dataRowStart - 1) {
                dataRow.getAndIncrement();
                return;
            }

            // 创建一个新的数据存储单元,并将通用数据拷贝至新的单元
            Map<String, Object> obj = new HashMap<>();
            absRes.forEach(obj::put);

            /*
             * 遍历每个数据项
             */
            item.forEach((key, value) -> {
                String fieldName = colFields.get(key);
                /*
                    此处验证是为了解决null类型无法json化
                 */
                if (StringUtils.isBlank(fieldName)) {
                    return;
                }

                if (!obj.containsKey(fieldName)) {
                    obj.put(fieldName, StringUtils.isBlank(value) ? "" : value);
                }
            });

            res.add(obj);
            dataRow.getAndIncrement();
        });

        return res;
    }
}
